package app.socket.client;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import app.htby.utils.IOTSUtils;
import app.htby.utils.SocketUtils;

/**
 * 控制系统的 socket异步连接
 * 
 * @author lfy.xys
 * @date 2018年5月22日
 *
 */
public class CtrlConnClientRunnable implements Runnable {
	
	private static Logger logger = LoggerFactory.getLogger(CtrlConnClientRunnable.class);
	
	private String runnableId;// 当前线程的id
	private String ip;
	private int port;
	private byte[] sendMsg;// 发送的消息
	private ByteBuffer rBuffer = ByteBuffer.allocate(1024 * 9);
	private int count = 0;
	
	
	public CtrlConnClientRunnable(String runnableId, String ip, int port, byte[] sendMsg) {
		this.runnableId = runnableId;
		this.ip = ip;
		this.port = port;
		this.sendMsg = sendMsg;
	}

	public void run() {
		try {
			logger.info(ip + ":" + port);
			InetSocketAddress SERVER = new InetSocketAddress(ip, port);
			SocketChannel socketChannel = SocketChannel.open();
			socketChannel.configureBlocking(false);
			Selector selector = Selector.open();
			socketChannel.register(selector, SelectionKey.OP_CONNECT);
			socketChannel.connect(SERVER);
			SocketChannel client = null;

			while (true) {
				selector.select();
				Set<SelectionKey> keySet = selector.selectedKeys();
				for (final SelectionKey selectionKey : keySet) {
					if (selectionKey.isConnectable()) {
						client = (SocketChannel) selectionKey.channel();
						if (client.isConnectionPending()) {
							try {
								client.finishConnect();
							} catch (Exception e) {
								reconnect();
							}
							logger.info("connect success ! sendMsg : {}", sendMsg.toString());
							//发送数据
							client.write(ByteBuffer.wrap(sendMsg));
						}
						client.register(selector, SelectionKey.OP_READ);
					} else if (selectionKey.isReadable()) {
						boolean failure = true;
						client = (SocketChannel) selectionKey.channel();
						rBuffer.clear();
						try {
							count = client.read(rBuffer);
							logger.info("count:" + count);
							failure = false;
						} catch (Exception e) {
							logger.info("断开连接");
//							e.printStackTrace();
						}
						
						if (count <= 0 || failure) {
							selectionKey.cancel(); // Some JDK implementations run into an infinite loop without this.
							logger.info("服务端关闭");
						} else {
							//重新读取 指定长度的数据。 否则获取的是创建的缓冲区长度，末尾会被补0。
							rBuffer.clear();
							byte[] bytes = new byte[count];
							rBuffer.get(bytes, 0, bytes.length);
							
//							client = (SocketChannel) selectionKey.channel();
							client.register(selector, SelectionKey.OP_READ);
							
							//将结果放入集合
							IOTSUtils.ctrlResultMap.put(runnableId, bytes);
							logger.info(bytes.toString());
							
							//关闭 当前socket连接
							client.close();
							logger.info("关闭 {}:{} 的连接", ip, port);
						}
					}  
				}
				keySet.clear();
			}

		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	/**
	 * 服务端连接不上，客户端断线重连
	 * 
	 * @author lfy.xys
	 * @date 2018年5月30日
	 *
	 */
	public void reconnect() {
		logger.info("服务端连接失败，{}ms后进行重连...", SocketUtils.NIO_RECONNECT_INTERVAL_TIME);
		try {
			Thread.sleep(SocketUtils.NIO_RECONNECT_INTERVAL_TIME);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		this.run();
	}

	public static void main(String[] args) {
		CtrlConnClientRunnable r = new CtrlConnClientRunnable("11","localhost", 8888, "bbb在政治".getBytes());
		r.run();
	}

}
